/*
 * libdlm.c
 */

#include "libdlm.h"
#include "request.h"
#include "reply.h"
#include "dlmd.h"
#include "log.h"

#include <sys/types.h>
#include <sys/stat.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <fcntl.h>

static int output;		// output fifo descriptor
static int input;		// input fifo descriptor

struct reply * get_reply();
void init_fifo_in();
int send_request(struct request *req);
struct request * init_request(resource_id_t resource_id, lock_type_t lock_type, timeout_t timeout, int type, int path_lenght);
//TODO free()
 int DLM_init(char *path, resource_id_t *resource_id){

    struct request* req =  init_request(0/*unused*/, -1/*unused*/, DLM_timeout_inf, DLMD_REQ_REGISTER, strlen(path));
    struct reply * reply_from_dlm;
    req->path = ( char* ) malloc( strlen(path) + 1 );
    memcpy(req->path, path, strlen(path));
    //znak konca
    req->path[strlen(path)] = '\0';

    send_request(req);
    init_fifo_in();
    reply_from_dlm = get_reply();

    if( reply_from_dlm->code == DLM_reply_OK )
        *resource_id = reply_from_dlm->resource_id;

    return reply_from_dlm->code;
 }

 int DLM_lock(resource_id_t resource_id, lock_type_t lock_type, timeout_t timeout)
 {
    struct request* req = init_request(resource_id, lock_type, timeout, DLMD_REQ_LOCK, 0);
    //TODO czy funkcja init_fifo_in() jest przygotowana na wielokrotne wywołanie przez proces?
    init_fifo_in();
    send_request(req);
    struct reply * reply_from_dlm = get_reply();

    return reply_from_dlm->code;
 }

 int DLM_unlock(resource_id_t resource_id)
 {
    struct request* req = init_request(resource_id, 0/*unused*/, DLM_non_block, DLMD_REQ_UNLOCK, 0);
    send_request(req);
    struct reply * reply_from_dlm = get_reply();

    return reply_from_dlm->code;
 }

 int DLM_trylock(resource_id_t resource_id, lock_type_t lock_type)
 {

    struct request* req =  init_request(resource_id, lock_type, DLM_non_block, DLMD_REQ_TRYLOCK, 0);
    //TODO czy funkcja init_fifo_in() jest przygotowana na wielokrotne wywołanie przez proces?
    init_fifo_in();
    send_request(req);
    struct reply * reply_from_dlm= get_reply();

    return reply_from_dlm->code;
 }

 struct request * init_request(resource_id_t resource_id, lock_type_t lock_type, timeout_t timeout, int type, int path_lenght){

    struct request *req = malloc(sizeof(struct request) + path_lenght +1);
    req->pid=getpid();
    req->timeout=timeout;
    req->lock_type=lock_type;
    req->size=sizeof(struct request) + path_lenght +1;
    req->type=type;
    req->id=resource_id;

    return req;
 }

int send_request(struct request *req){

    char path[DLMD_BUFF_SIZE];
    strcpy( path, opt_working_directory );
    strcat( path, DLMD_INPUT_PIPE_NAME );
    if ( ( output = open( path, O_RDONLY ) ) == -1 ) {
        err("cannot open input fifo: %s", strerror(errno));
        //TODO a tu co?
        dlmd_exit(1);
    }

    write(output, req, sizeof(req) + strlen(req->path));
    //TODO dodac wartosc zwracana
    return 0;
 }

void init_fifo_in(){
    pid_t pid = getpid();

    char pid_name[10];
    sprintf(pid_name, "%u", pid);
    char path[DLMD_BUFF_SIZE];
    strcpy( path, opt_working_directory );
    strcat( path, pid_name );

   if (mkfifo(path, S_IFIFO | S_IRWXU | S_IRWXG | S_IRWXO))
      oops("creating fifo error: %s", strerror(errno));

    debug2("opening fifo `%s'", path);
    info("listening...");
    if ((input = open(path, O_RDONLY)) == -1) {
        err("cannot open input fifo: %s", strerror(errno));
        //TODO a tu co?
        dlmd_exit(1);
    }
}

struct reply * get_reply(){

    byte_t *ptr = ( byte_t* ) malloc( DLMD_BUFF_SIZE * sizeof( byte_t ) );
    int bytes;

    bytes = read(input, ptr, DLMD_BUFF_SIZE-1);
    debug2("read request/s, lenght: %d bytes", bytes);
    return ((struct reply*) ptr);
 }
